package br.com.paulucci.caio.jpautils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

/**
 *
 * @author Caio Rodrigo Paulucci
 */
public class Util {

    private List<String> objetosPassados = null;

    public Util() {
    }

    public static Locale getDefaultLocale() {
        return new Locale("pt", "BR");
    }

    public static Boolean isNullOrEmpty(String s) {
        if (s == null) {
            return true;
        } else if (s.equals("")) {
            return true;
        }
        return false;
    }

    public static String preencheZerosAEsquerda(String str, Integer qtde) {
        String s = str.trim();
        while (s.length() < qtde) {
            s = '0' + s;
        }
        return s;
    }

    public static String preencheZerosAEsquerda(String str) {
        String s = str.trim();
        while (s.length() < 14) {
            s = '0' + s;
        }
        return s;
    }

    public Boolean chamaAFuncaoSetaNullNosCamposObjetoVazios(Object objeto) {
        Boolean retorno = false;
        Field[] campos = objeto.getClass().getDeclaredFields();
        for (Field field : campos) {
            //String objetoPassado = objeto.getClass().getSimpleName() + "+" + field.getName();
            //if (!this.getObjetosPassados().contains(objetoPassado)) {
            if ((field.getModifiers() != Modifier.STATIC) && (field.getModifiers() != Modifier.FINAL) && (field.getModifiers() != 10) && (field.getModifiers() != 9)) {
                if (field.getType().equals(String.class)) {
                    if (this.invocaGetDoCampoString(field, objeto)) {
                        retorno = true;
                    }

                } else {
                    if (this.invocaGetDoCampo(field, objeto)) {
                        retorno = true;
                    }
                }
            }
        //this.getObjetosPassados().add(objetoPassado);
        //}
        }
        return retorno;
    }

    public Boolean setaNullNosCamposObjetoVazios(Object objeto) {
        this.setObjetosPassados(new LinkedList<String>());
        return chamaAFuncaoSetaNullNosCamposObjetoVazios(objeto);
    }

    public Boolean chamaAFuncaoInstanciaSubCamposVazios(Object objeto) {
        Field[] campos = objeto.getClass().getDeclaredFields();
        for (Field field : campos) {
            /*String objetoPassado = objeto.getClass().getSimpleName() + "+" + field.getName();
            if (!this.getObjetosPassados().contains(objetoPassado)) {*/
            if ((field.getModifiers() != Modifier.STATIC) && (field.getModifiers() != Modifier.FINAL) && (field.getModifiers() != 10) && (field.getModifiers() != 9)) {
                if (!field.getType().equals(String.class)) {
                    if (this.instanciaOCampo(field, objeto)) {
                        return true;
                    }
                }
            }
        //this.getObjetosPassados().add(objetoPassado);
        //}
        }
        return false;
    }

    public Boolean instanciaOsSubCamposVazios(Object objeto) {
        this.setObjetosPassados(new LinkedList<String>());
        return chamaAFuncaoInstanciaSubCamposVazios(objeto);
    }

    private Boolean invocaGetDoCampo(Field field, Object objeto) {
        Boolean retorno = false;
        String nome = field.getName();
        String novoNome = field.getName().substring(1);
        novoNome = field.getName().substring(0, 1).toUpperCase() + novoNome;
        try {
            Boolean existeOMetodo = false;
            for (Method metodo : objeto.getClass().getDeclaredMethods()) {
                if (metodo.getName().equals("get" + novoNome)) {
                    existeOMetodo = true;
                }
            }
            if (existeOMetodo) {
                Method metodo = objeto.getClass().getMethod("get" + novoNome, new Class[0]);
                Object objRet = (Object) metodo.invoke(objeto, new Object[0]);
                if (objRet != null) {
                    Class classeDoCampo = objRet.getClass();
                    Boolean temAAnnotation = false;
                    if (classeDoCampo.isAnnotationPresent(ClasseInstanciavel.class)) {
                        temAAnnotation = true;
                    }
                    if (!temAAnnotation) {
                        if (field.getType().equals(List.class)) {
                            if (objRet != null) {
                                if (((List) objRet).isEmpty()) {
                                    Object objNull;
                                    objNull = null;
                                    Method mSet = objeto.getClass().getMethod("set" + novoNome, field.getType());
                                    mSet.invoke(objeto, objNull);
                                    return false;
                                }
                            }
                            return true;
                        } else if (field.getType().equals(Integer.class)) {
                            /*if (((Integer) objRet) == 0) {
                                Object objNull;
                                objNull = null;
                                Method mSet = objeto.getClass().getMethod("set" + novoNome, field.getType());
                                mSet.invoke(objeto, objNull);
                                return false;
                            } else {
                                return true;
                            }*/
                        } else if (field.getType().equals(Double.class)) {
                            /*if (((Double) objRet) == null) {
                            Object objNull;
                            objNull = null;
                            Method mSet = objeto.getClass().getMethod("set" + novoNome, field.getType());
                            mSet.invoke(objeto, objNull);
                            return false;
                            } else {*/
                            return true;
                        //}
                        }
                    } else {
                        String objetoPassado = objRet.getClass().getSimpleName() + "_" + field.getName();
                        //System.out.println("Vai verificar c o objeto ja foi visto e o objetos passados tem " + this.getObjetosPassados().size() + " itens ");
                        if (!this.getObjetosPassados().contains(objetoPassado)) {
                            this.getObjetosPassados().add(objetoPassado);
                            //System.out.println("Nao contem na lista = " + objetoPassado);
                            if (!this.chamaAFuncaoSetaNullNosCamposObjetoVazios(objRet)) {
                                Object objNull = field.getType().newInstance();
                                objNull = null;
                                Method mSet = objeto.getClass().getMethod("set" + novoNome, field.getType());
                                mSet.invoke(objeto, objNull);
                                return false;
                            } else {
                                return true;
                            }
                        } else {
                            //System.out.println("Ja contem na lista = " + objetoPassado);
                            return false;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    private Boolean invocaGetDoCampoString(Field field, Object objeto) {
        String nome = field.getName();
        String novoNome = field.getName().substring(1);
        novoNome = field.getName().substring(0, 1).toUpperCase() + novoNome;

        try {
            Method metodo = objeto.getClass().getMethod("get" + novoNome, new Class[0]);
            String strRet = (String) metodo.invoke(objeto, new Object[0]);
            if (!Util.isNullOrEmpty(strRet)) {
                return true;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    private Boolean instanciaOCampo(Field field, Object objeto) {
        String nome = field.getName();
        String novoNome = field.getName().substring(1);
        Boolean retorno = false;
        novoNome = field.getName().substring(0, 1).toUpperCase() + novoNome;
        try {
            Method metodo = objeto.getClass().getMethod("get" + novoNome, new Class[0]);
            Object objRet = (Object) metodo.invoke(objeto, new Object[0]);
            Class classeDoCampo = field.getType();
            Boolean temAAnnotation = false;
            if (classeDoCampo.isAnnotationPresent(ClasseInstanciavel.class)) {
                temAAnnotation = true;
            }
            if (field.getType().equals(List.class)) {
                Type tipoDaLista = null;
                Type tipo = field.getGenericType();
                if (tipo instanceof ParameterizedType) {
                    ParameterizedType tipoDoParametro = (ParameterizedType) tipo;
                    for (Type object : tipoDoParametro.getActualTypeArguments()) {
                        tipoDaLista = object;
                    }
                }
                Object objNew = null;
                if (tipoDaLista == null) {
                    objNew = new LinkedList();
                } else {
                    String nomeDoTipo = tipoDaLista.toString().replace("class", "").trim();
                    objNew = Class.forName("java.util.LinkedList").newInstance();
                }
                if (objRet == null) {
                    Method mSet = objeto.getClass().getMethod("set" + novoNome, field.getType());
                    mSet.invoke(objeto, objNew);
                }
            } else if (temAAnnotation) {
                //---------------------------------------------------------------
                String objetoPassado = objeto.getClass().getSimpleName() + "_" + field.getName();
                //System.out.println("Vai verificar c o objeto ja foi visto e o objetos passados tem "+this.getObjetosPassados().size()+" itens ");
                if (!this.getObjetosPassados().contains(objetoPassado)) {
                    //System.out.println("Nao contem na lista = " + objetoPassado);
                    Object objNew = field.getType().newInstance();
                    this.getObjetosPassados().add(objetoPassado);
                    if (objRet == null) {
                        Method mSet = objeto.getClass().getMethod("set" + novoNome, field.getType());
                        mSet.invoke(objeto, objNew);
                        if (!this.chamaAFuncaoInstanciaSubCamposVazios(objNew)) {
                            retorno = false;
                        } else {
                            retorno = true;
                        }
                    } else {

                        if (!this.chamaAFuncaoInstanciaSubCamposVazios(objRet)) {
                            retorno = false;
                        } else {
                            retorno = true;
                        }

                    }
                } else {
                    //System.out.println("Ja contem na lista = " + objetoPassado);
                    retorno = false;
                }

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return retorno;
    }

    public List<String> getObjetosPassados() {
        if (objetosPassados == null) {
            objetosPassados = new LinkedList<String>();
        }
        return objetosPassados;
    }

    public void setObjetosPassados(List<String> objetosPassados) {
        this.objetosPassados = objetosPassados;
    }
}
